﻿using System;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

class Program
{
    private static bool _isRunning = false;
    private const string _ipAddress = "127.0.0.1";
    private static int _port = 9527;
    private static TcpClient _client;
    private static NetworkStream _stream;

    static async Task Main()
    {
        Console.WriteLine($"IM client now will connect to server: {_ipAddress} port: {_port}");

        Start();
        new Task(async () => await StartClient()).Start();

        while (_isRunning)
        {
            var input = Console.ReadLine();
            if (input == "exit")
            {
                await Stop();
                Console.WriteLine("IM client now will exit the connection");
            }
            else
            {
                await Send(input);
            }
        }
    }

    static void Start() => _isRunning = true;
    static async Task Stop()
    {
        _isRunning = false;
        if (_client != null)
        {
            while (_client.Connected)
            {
                _stream.Close();
                _stream.Dispose();
                _client.Close();
                _client.Dispose();
                await Task.Delay(1000);
            }
        }

        Console.WriteLine($"连接到服务器端 {_port} 端口已关闭!");
    }

    /// <summary>
    /// 客户端连接到服务器端固定端口号, 以获取下次连接使用的随机端口
    /// </summary>
    /// <returns></returns>
    static async Task StartClient()
    {
        while (_isRunning && (_client == null || !_client.Connected))
        {
            try
            {
                _client = new TcpClient(_ipAddress, _port);
                if (_client.Connected)
                {
                    Console.WriteLine($"已连接到服务器端 {_port} 端口!");

                    _stream = _client.GetStream();
                    if (_stream.CanRead)
                    {
                        var buffer = new byte[16];
                        await _stream.ReadAsync(buffer);
                        var port = BitConverter.ToInt32(buffer);
                        if (port >= 10000 && port <= 20000)
                        {
                            Console.WriteLine($"已获得随机端口 {port}, 将使用新的端口号建立链接!");

                            _stream.Close();
                            _stream.Dispose();
                            _client.Close();

                            new Task(async () => await Connect(port), TaskCreationOptions.LongRunning).Start();

                            break;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                if (_isRunning)
                {
                    Console.WriteLine($"无法连接到服务器端 {_port} 端口, 5秒钟后再试!");
                    await Task.Delay(5000);
                }
            }
        }
    }

    /// <summary>
    /// 客户端链接到服务器端随机端口号 port
    /// </summary>
    /// <param name="port"></param>
    /// <returns></returns>
    static async Task Connect(int port)
    {
        _client = new TcpClient(_ipAddress, port);
        if (_client.Connected)
        {
            _port = port;
            _stream = _client.GetStream();
            var buffer = new byte[1024];
            Console.WriteLine($"已连接到服务器端 {port} 端口, 将使用端口 {port} 进行通信!");

            while (_isRunning)
            {
                try
                {
                    if (_stream.CanRead)
                    {
                        Console.WriteLine($"开始等待读取来自服务器端的消息");
                        var length = await _stream.ReadAsync(buffer);
                        var message = Encoding.UTF8.GetString(buffer, 0, length);
                        if (message != null && !string.IsNullOrWhiteSpace(message))
                        {
                            Console.WriteLine($"收到来自服务器端的消息: {message}");
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    if (_isRunning)
                    {
                        new Task(async () => await StartClient()).Start();
                        break;
                    }
                }
                finally
                {
                    await Task.Delay(100);
                }
            }
        }
    }

    /// <summary>
    /// 发送消息给服务器端
    /// </summary>
    /// <param name="message"></param>
    static async Task Send(string message)
    {
        if (_client.Connected && _stream.CanWrite)
        {
            Console.WriteLine($"将发送消息 {message} 到服务器端!");
            var buffer = Encoding.UTF8.GetBytes(message);
            await _stream.WriteAsync(buffer, 0, buffer.Length);
            Console.WriteLine($"已发送消息 {message} 到服务器端!");
        }
    }
}